/*
 *  Trabalho 4 - Processamento de Imagens (scc0251) 
 *  Prof. Moacir P. Ponti Jr --- ICMC/USP
 * 
 *  Transformacoes morfologicas
 * 
 *  Autor: Vinicius de Oliveira Boen	7152163
 *  Autor: Lohan 
 */

#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <math.h>

IplImage * Threshold(IplImage *im, int L, int inv){

	int x, y, cont=0;
	int step = im->widthStep;

	// Cria imagem que sera transformada e retornada pela funcao
	IplImage *im2 = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
	// ponteiro para as duas imagens
	uchar *dataim2 = (uchar*)im2->imageData;
    	uchar *data = (uchar*)im->imageData;

	//printf("Digite o valor da limirizacao ou digite -1:\n");
	//scanf("%d", &L);

	
	// No caso de -1 o 'L' sera calculado 
	// sera preciso percorrer a imagem somando os valores para achar a intensidade media
	if(L == -1){
		for(y=0; y < im->height; y++){
			for (x=0; x < im->width; x++){
				cont += data[x+step*y];
			}
		}
	L = cont / (im->height * im->width);
	//printf("L: %d \nHeight: %d \nWidth: %d \n", L, im->height, im->width);
	}

	//funcao de transformacao binaria
	for (y=0; y < im->height; y++){
		for (x=0; x < im->width; x++){
			if (data[x+step*y] >= L && inv == 1)
				dataim2[x+step*y] = 0;
			if (data[x+step*y] >= L && inv == 0)
				dataim2[x+step*y] = 255;
			if (data[x+step*y] < L && inv == 1)
				dataim2[x+step*y] = 255;
			else
				dataim2[x+step*y] = 0;
		}
	}

	return im2;
}


// Suaviza imagem
IplImage* SuavizacaoMedia(IplImage* im, int N) 
{
	CvSize imgSize = cvGetSize(im);

	// Imagem de saida
	IplImage* ret = cvCreateImage(imgSize, im->depth, 1);
	// copia imagem original
	cvCopy(im, ret, NULL);
	
	int x, y;
	int s, t;
	int a;
	
	a = (N-1)/2;
	//printf("Filtragem por media, filtro tamanho %d x %d", N, N);
	
	// imagem entrada data
	uchar *data = (uchar*)im->imageData;
	int step = im->widthStep;

	// imagem saida data
	uchar *data2 = (uchar*)ret->imageData;
	
	// percorre pixel a pixel para realizar filtragem (evitando a borda)
	for (x = a; x < imgSize.height-a; x++) {
	   for(y = a; y < imgSize.width-a; y++) {
		// filtragem do pixel (x,y)
		int newpixel = 0;
		for (s = x-a; s <= (x+a); s++) { 
		    for (t = y-a; t <= (y+a); t++) { 
			newpixel += data[s * step + t];
		    }
		}
		data2[x * step + y] = (uchar) (newpixel/(N*N));
	   }
	}
	return ret;
}

/*Primeiro foi feito uma limirizacao com limite de 80 e ao mesmo tempo a feita a inversao da imagem, em seguida uma dilatacao com uma interacao e tamanho 3 de circulo para resaltar os caminhos de vento, em seguida foi feita uma erosao com 
uma interacao e tamanho 3x3 para tirar o excesso causado pela dilatacao e por final foi feito um fechamento 3x3 com uma interacao fechar os "buracos"  
*/
IplImage* sequencia1(IplImage* im, int inv){
	
	IplImage *aux2, *aux3, *aux4, *aux5;//cria imagem auxiliares 2, 3 e 4
	aux2 = cvCreateImage(cvGetSize(im), im->depth, 1);
	aux3 = cvCreateImage(cvGetSize(im), im->depth, 1);
	aux4 = cvCreateImage(cvGetSize(im), im->depth, 1);
	aux5 = cvCreateImage(cvGetSize(im), im->depth, 1);

	aux2 = Threshold(im, 80, inv);
	//cvThreshold( im, aux2, 90, 255, CV_THRESH_BINARY);

	IplConvKernel *stelem_c, *stelem_r;// cria elementos estruturantes
	int pc = (3-1)/2;
      	stelem_c = cvCreateStructuringElementEx(3,3, pc,pc, CV_SHAPE_ELLIPSE, NULL);//circulo
	stelem_r = cvCreateStructuringElementEx(3,3, 1,1, CV_SHAPE_RECT, NULL);//quadrado

	// dilatacao passando im1 por parametro e retornando im2, com o elemento stelem_c ja criado
      	// 1 eh o numero de vezes que sera aplicada a dilatacao
      	cvDilate(aux2, aux3, stelem_c, 1);
      	fprintf(stdout,"Dilatacao...\n");
      	fflush(stdout);

      	// erosao passando im1 por parametro e retornando im2, com o elemento stelem_r ja criado
      	// 3 eh o numero de vezes que sera aplicada a erosao
      	cvErode(aux3, aux4, stelem_r, 1);
      	fprintf(stdout,"Erosao...\n");
      	fflush(stdout);

      	// fechamento usando stelem_c e 1 iteracao
      	cvMorphologyEx(aux4, aux5, NULL, stelem_c, CV_MOP_CLOSE, 1);
      	fprintf(stdout,"Fechamento...\n");
      	fflush(stdout);

	cvReleaseImage(&aux2);
	cvReleaseImage(&aux3);
	cvReleaseImage(&aux4);
      	cvReleaseStructuringElement(&stelem_c);
      	cvReleaseStructuringElement(&stelem_r);	

	return aux5;	
}

/*Primeiro eh aplicada a suavizacao pela media, para diminuir a ruidosidade da imagem
em seguida eh feita a limerizacao com limitante de 90. Com isso onde ficar branco significa
 que tem celulas, entao a feita a abertura para alisar a margem e a erosao para tirar o
 excesso causado pela abertura*/
IplImage* sequencia2(IplImage* im, int inv){
	
	IplImage *aux2, *aux3, *aux4;//cria imagem auxiliares 2, 3 e 4
	aux2 = cvCreateImage(cvGetSize(im), im->depth, 1);
	aux3 = cvCreateImage(cvGetSize(im), im->depth, 1);
	aux4 = cvCreateImage(cvGetSize(im), im->depth, 1);

	aux2 = SuavizacaoMedia(im, 5);
	//cvSmooth( aux, aux2, CV_MEDIAN, 5, 0);

	aux2 = Threshold(aux2, 90, inv);
	//cvThreshold( im, aux2, 90, 255, CV_THRESH_BINARY);

	IplConvKernel *stelem_c, *stelem_r;// cria elementos estruturantes
	int pc = (3-1)/2;
      	stelem_c = cvCreateStructuringElementEx(3,3, pc,pc, CV_SHAPE_ELLIPSE, NULL);//circulo
	stelem_r = cvCreateStructuringElementEx(5,5, 2,2, CV_SHAPE_RECT, NULL);//quadrado

      	// abertura usando stelem_c e 3 iteracoes
      	cvMorphologyEx(aux2, aux3, NULL, stelem_c, CV_MOP_OPEN, 1);
      	fprintf(stdout,"Abertura...\n");
      	fflush(stdout);

      	// erosao passando im2 por parametro e retornando im3, com o elemento stelem_r ja criado
      	// 1 eh o numero de vezes que sera aplicada a erosao
      	cvErode(aux3, aux4, stelem_r, 1);
      	fprintf(stdout,"Erosao...\n");
      	fflush(stdout);

	cvReleaseImage(&aux2);
	cvReleaseImage(&aux3);
      	cvReleaseStructuringElement(&stelem_c);
      	cvReleaseStructuringElement(&stelem_r);	

	return aux4;
}

//IplImage* sequencia3(IplImage* im){}


int main(int argc, char *argv[])
{
	const char *file1, *file2;
	file1 = argv[1];
	file2 = argv[2];
	int opc = atoi(argv[3]);// 1 = imagem de marte, 2 = imagem microscopica, 3 = imagem de satelite
	int inv = atoi(argv[4]);// 1 para inverter ou 0 para nao inverter

	IplImage *im1, *im2;
	im1 = cvLoadImage( file1, CV_LOAD_IMAGE_GRAYSCALE);//carrega imagem 1

      	if (!im1) {
	  	printf("Nao foi possivel carregar imagem de entrada\n");
	  	return -1;
      	}
	// cria imagem 2
	im2 = cvCreateImage(cvGetSize(im1), im1->depth, 1);
	
	switch(opc){
		case 1:
			im2 = sequencia1(im1, inv);
		break;
		case 2:
			im2 = sequencia2(im1, inv);
		break;
		case 3:
			//im2 = sequencia3(im1);
		break;	
	}

	// cria uma janela para exibir a imagem
      	cvNamedWindow("Entrada",0);
      	cvNamedWindow("Saida",0);
	// exibe as imagens nas janelas
	cvShowImage("Entrada", im1);
      	cvShowImage("Saida", im2);

	cvWaitKey(0); // aguarda o usuario digitar uma tecla

      	// grava no disco a imagem processada
      	cvSaveImage(file2, im2, 0);

	// libera a memoria alocada para as imagens
      	cvReleaseImage(&im1);
      	cvReleaseImage(&im2);


}
